#include "Block.h"

bool equalBytes(byte* a, byte* b, uShort n) {
	for (uShort i = 0; i < n; i++) {
		if (a[i] != b[i]) {
			return false;
		}
	}
	return true;
}

uShort bytesToInt2(const byte* bytes)
{
	return ((bytes[0] & 0xFF) << 8) | bytes[1];
}

uInt bytesToInt3(const byte* bytes)
{
	return ((((bytes[0] & 0xFF) << 8) | bytes[1]) << 8) | bytes[2];
}

uInt bytesToInt4(const byte* bytes)
{
	return ((((((bytes[0] & 0xFF) << 8) | bytes[1]) << 8) | bytes[2]) << 8) | bytes[3];
}

void int2ToBytes(byte* bytes, uShort i)
{
	bytes[0] = (i >> 8) & 0xFF;
	bytes[1] = i & 0xFF;
}

void int3ToBytes(byte* bytes, uInt i)
{
	bytes[0] = (i >> 16) & 0xFF;
	bytes[1] = (i >> 8) & 0xFF;
	bytes[2] = i & 0xFF;
}

void int4ToBytes(byte* bytes, uInt i)
{
	bytes[0] = (i >> 24) & 0xFF;
	bytes[1] = (i >> 16) & 0xFF;
	bytes[2] = (i >> 8) & 0xFF;
	bytes[3] = i & 0xFF;
}


sRowHeader Block::getRowHeader(rowId rId)
{
	return{ { _id, rId }, (bytes + ROW_HEADER_SIZE * rId + HEADER_SIZE) };
}

sRow Block::getNewRow(uShort rowSize)
{
	if (bytes[0] & 0x40)	//neue row kann grundsaetlich nicht eingefuegt werden 11------
		throw ERR_BLOCK_CLOSED;
	uShort rowCount = getRowCount();
	rowId rId = 0;
	sRowHeader rowHeader;
	sRow row;
	//suche freien RowHeader
	while (rId < rowCount)
	{
		rowHeader = getRowHeader(rId);
		if (!(rowHeader.bytes[0] & 0x80))
		{	//row ist frei
			if (rowSize)
			{	//Speicherplatz wird benoetigt
				if ((rowHeader.bytes[0] & 0x40) || (bytesToInt2(rowHeader.bytes + 3) <= rowSize))	//ausreichend Speicherplatz vorhanden
					break;
			}
			else //Speicherplatz egal
				break;
		}
		rId++;
	}
	uShort freeSpace = getFreeSpace();
	if (rId == rowCount) //neuen RowHeader erzeugen
	{
		rowCount++;
		freeSpace -= ROW_HEADER_SIZE;
		if (rowSize)
		{	//Speicherplatz wird benoetigt
			if (freeSpace < rowSize + ROW_HEADER_SIZE)
			{	//nicht mehr genug Speicher im Block
				bytes[0] = bytes[0] | 0x40;	//sperrt den Block fuer neue Rows
				throw ERR_INSUFFICIENT_STORAGE;
			}
			rowHeader = getRowHeader(rId);
			rowHeader.bytes[0] = 0x80; //1000 0000
			freeSpace -= rowSize;
			uShort offset = rowHeader.bytes - bytes + ROW_HEADER_SIZE + freeSpace;
			int2ToBytes(rowHeader.bytes + 1, offset);	//Offset
			int2ToBytes(rowHeader.bytes + 3, rowSize);	//rowspace
			rowHeader.bytes[5] = 0x0;
			row = { rowHeader, (bytes + offset) };
		}
		else
		{	//nur header einrichten
			rowHeader.bytes[0] = 0xc0; //1100 0000
			int3ToBytes(rowHeader.bytes + 1, 0);	//BID
			int2ToBytes(rowHeader.bytes + 4, 0);	//RID
			row = { rowHeader, NULL };
		}
		setRowCount(rowCount);
		setFreeSpace(freeSpace);
	}
	else {
		row = getRow(rowHeader);
	}
	return row;
}

sRow Block::getRow(sRowHeader rowHeader)
{
	return sRow{ rowHeader, (bytes + bytesToInt2(rowHeader.bytes + 1)) };
}

sRow Block::getRow(rowId rId)
{
	return getRow(getRowHeader(rId));
}

bool Block::isFree()
{
	return !(bytes[0] & 0x80);
}

void Block::setFree(bool free)
{
	if (free)
		bytes[0] = bytes[0] & 0x7F;
	else
		bytes[0] = bytes[0] | 0x80;
}

uShort Block::getRowCount()
{
	return bytesToInt2(bytes + 1);
}

void Block::setRowCount(uShort rowCount)
{
	int2ToBytes(bytes + 1, rowCount);
}

uShort Block::getFreeSpace()
{
	return bytesToInt2(bytes + 3);
}

void Block::setFreeSpace(uShort freeSpace)
{
	int2ToBytes(bytes + 3, freeSpace);
}

Block::Block(blockId id)
{
    _id = id;
	bytes = new byte[SIZE];
	bytes[0] = 0x80; //Modus: belegt
	int2ToBytes(bytes + 1, 0); //Zeilen: 0
	int2ToBytes(bytes + 3, INI_FREE_SPACE);	//initialer freier Speicherplatz
	for (int i = HEADER_SIZE; i < SIZE; i++)
	{
        bytes[i] = 0x00;
    }
}

Block::Block(blockId id, byte* b)
{
    _id = id;
    bytes = b;
	setFree(false);
}

Block::~Block()
{
    delete[] bytes;
}

void Block::print()
{
	std::cout << "-\t-\t-\t-\t-" << std::endl;
	//for (int i = 0; i < Block::SIZE; i++)
	//    lhs << (i > 0 ? "|" : "") << std::hex << static_cast<int>(b.bytes[i]);
	std::cout << "id:\t" << _id << std::endl;
	std::cout << "mod: " << std::hex << static_cast<int>(bytes[0]) << std::endl;
	std::cout << "rows:\t" << getRowCount() << std::endl;
	std::cout << "freespace:\t" << getFreeSpace() << std::endl;
	std::cout << "all rowheaders:" << std::endl;
	for (uShort rId = 0; rId < getRowCount(); rId++)
	{
		sRowHeader rowHeader = getRowHeader(rId);
		std::cout << "\tid: " << rId << std::endl;
		std::cout << "\tmod: " << std::hex << static_cast<int>(rowHeader.bytes[0]) << std::endl;
		std::cout << "\toffset: " << bytesToInt2(rowHeader.bytes + 1) << std::endl;
		std::cout << "\trowspace: " << bytesToInt2(rowHeader.bytes + 3) << std::endl;
	}

	std::cout << "-\t-\t-\t-\t-" << std::endl;
}


std::ostream& operator<<(std::ostream& lhs, Block const&b)
{
	lhs << "ARGH! WO ZUR HOELLE WIRD DAS AUFGERUFEN? Ich such hier schon seid Stunden...." << std::endl;
    return lhs;
}
